iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
WordPress

從 0 到 100:WordPress 開發者的實戰手冊系列 第 22

Day 22 - 理解 WordPress 的資料庫操作類別 wpdb 的用法

  • 分享至 

  • xImage
  •  

到目前為止,我們尚未寫過任何一行的 SQL 語法來取得資料,主要原因是 WordPress 的內建函式已把資料庫操作的部分都包裝起來,只需要傳參數到函式裡,就可以取得資料。

假如,想要更複雜一點的資料搜尋條件,使用在上一篇文章中所介紹的 WP_Query 類別,也可以解決大部分的情境,很少需要我們自行編寫 SQL 的部分。

依賴關係

內建的函式例如 wp_options 資料表中取得資料的 get_option,包含其它大部分的函式,會使用 wpdb 類別,處理包裝好的邏輯,轉換成查詢的 SQL 語法,呼叫 $wpdb 實例來執行。

到自定內容查詢用的 WP_Query 類別,底層的依賴的也是 wpdb 類別,來幫它執行資料庫操作。

資料傳遞過程的依賴關係可簡化為下圖。

SQL 查詢依賴關係
圖:SQL 查詢的依賴關係

上圖挑選幾個常見的函式和類別舉例它們和 wpdb 類別的依賴關係。

例如:

  • get_post 函式依賴 WP_Post 類別,但在該類別之中,最後還是使用 wpdb 類別來存取資料表。
  • get_posts 函式依賴 WP_Query 類別,但在該類別之中,最後同樣使用 wpdb 類別來讀取資料表。

而這些包裝進 wpdb 中的 SQL 語句,交給最底層的 PHP 擴展的套件處理。因為 mysql 擴展在 PHP 5.5 版本以後已停用,以當今的 PHP 演進情況來看,基本上都是交由 mysqli 來執行 SQL 語句。

註:之所以使用 $wpdb 來包裝,這是一種「依賴反轉」的設計模式,並不是多此一舉的行為。因此最底層的 mysqli 也可以被替換掉。在第 4 天的文章提到的 Drop-in,自行設計 db.php 進行接掌底層的實作即可。

類別:wpdb

雖然絕大部分的場景,WordPress 都有現成的函式、類別幫我們包裝好了,可以直接使用,但還是會有機會需要自行編寫 SQL 語法,這時候就需要使用到 wpdb 類別。

如何使用

在 WordPress 核心程式中,load.phprequire_wp_db 函式負責了 wpdb 類別的實例化工作,如下:

實例化 wpdb 類別
圖:實例化 wpdb 類別

這個函式將 wpdb 類別實例化之後,存到全域變數中。由於此函式在整個 WordPress 核心載入的一開始就會呼叫了,因次不管在佈景主題中,還是我們自行編寫的外掛中,只要使用全域變數 $wpdb 就可以開始進行資料庫的操作。

例如:

使用全域變數 $wpdb
圖: 使用全域變數 $wpdb

方法:prepare

function prepare( $query, ...$args ) {}

此方法返回格式化後的 SQL 查詢,其中佔位符已被給定的值替換。所有在 SQL 查詢中的資料都必須在執行 SQL 查詢之前使用此方法進行 SQL 轉義,以避免 SQL 注入攻擊。它的語法和 PHP 的 sprintf 相同。

參數說明:

  • $query (string):SQL 查詢模板,其中包含佔位符,如 %s(對於字串)和 %d(對於整數)。
  • ...$args:與佔位符相對應的值,這些值將被安全地插入查詢中。

範例:

使用 prepare 方法進行 SQL 轉義
圖: 使用 prepare 方法進行 SQL 轉義

查詢參數接受和 PHP 函式 sprintf 的佔位符:

  • %s:字串
  • %d:整數)
  • %f:(浮點數

方法:get_var

function get_var( $query = null, $x = 0, $y = 0 ) {}

從資料庫中檢索單一變數值。使用的情境為,只需要從查詢中獲得單一值時。

參數說明:

  • $query (string):SQL查詢字串。
  • $x (int):要檢索的列的索引,預設為 0。
  • $y (int):要檢索的行的索引,預設為 0。

如果查詢返回多行和多列,可以使用 $x$y 參數來指定想要的值的確切位置。

範例:

使用 get_var 方法取得單一值
圖: 使用 get_var 方法取得單一值

查詢 WordPress 資料庫中的 wp_options 資料表,並取得 siteurl 選項的值。以上範例將輸出 WordPress網站的 URL。

方法:get_row

function get_row( $query = null, $output = OBJECT, $y = 0 ) {}

用於從資料庫中檢索單行。

參數說明:

  • $query (string):SQL查詢字串。
  • $output (string|constant):期望的返回格式。它可以是以下之一:
    • OBJECT:返回結果為物件 (這是預設值)。
    • ARRAY_A:返回結果為關聯陣列。
    • ARRAY_N:返回結果為索引陣列。
  • $y (int):要檢索的行的索引,預設為 0。

此方法根據 $output 參數的值以不同的格式返回特定行的資料。

範例:

使用 get_row方法取得單行資料
圖: 使用 get_row方法取得單行資料

查詢 WordPress 資料庫中的 wp_posts 資料表,並獲取 ID 為 5 的文章的所有資料,以上範例將輸出文章的標題。

方法:get_col

function get_col( $query = null, $x = 0 ) {}

從資料表中檢索某一欄 (column) 的所有值。

參數說明:

  • $query (string):SQL 查詢字串。
  • $x (int):你想要檢索的欄位的數字索引。例如,0 將會是查詢結果中的第一欄位,1 將會是第二欄位,依此類推。預設值為 0

此方法返回一個陣列,其中包含查詢結果中指定列的所有值。

範例:

使用 get_col 方法取得單欄資料
圖:使用 get_col 方法取得單欄資料

以上範例從 WordPress 資料庫的 wp_posts 表中獲取所有文章的標題。如果要取得日期,則只要在第二參數傳入索引 1,欄位加上 post_date,即可取得。

方法:get_results

function get_results( $query = null, $output = OBJECT ) {}

從資料表中檢索查詢的多行結果。

參數說明:

  • $query (string):SQL查詢字串。
  • $output (string|constant):期望的返回格式。它可以是以下之一:
    • OBJECT:返回結果為物件 (這是預設值)。
    • ARRAY_A:返回結果為關聯陣列。
    • ARRAY_N:返回結果為數字索引陣列。

此方法根據 $output 參數的值以不同的格式返回多行的資料。

範例:

使用 get_results 方法取得多行資料
圖:使用 get_results 方法取得多行資料

查詢 WordPress 資料庫中的 wp_posts 表,並獲取所有文章的標題和日期。
在上面的範例中,$posts_data 是一個陣列,每一行為物件,每個物件都有 post_titlepost_date 屬性,代表查詢結果中的每一行。

方法:insert

function insert( $table, $data, $format = null ) {}

用於將新資料插入到指定的資料庫表中。

參數說明:

  • $table (string):目標資料表的名稱。
  • $data (array):要插入的資料,其中鍵是欄位名稱,值是對應的資料的值。
  • $format (array|string):可選。描述 data 格式。可以是一個單一的格式字串(例如,%d%s 等),或者是一個格式的陣列。如果未指定,會自動判斷格式。

成功時返回 true,失敗時返回 false

範例:

使用 insert 方法新增資料
圖:使用 insert 方法新增資料

在 WordPress 資料庫的 wp_options 表中插入一個新的選項。在上述範例中,我們定義了一個 $data 陣列,其中包含要插入的資料使用 insert 方法,將資料插入到 wp_options 資料表中。

方法:update

function update( $table, $data, $where, $format = null, $where_format = null ) {}

修改、更新資料表中的資料。

參數說明:

  • $table (string):目標資料表的名稱。
  • $data (array):要更新的資料,其中鍵是欄位名稱,值是對應資料的值。
  • $where (array):用於確定哪些行應該被更新的條件。鍵是欄位名稱,值是對應的資料。
  • $format (array|string):可選。描述 data 格式。可以是一個單一的格式字串(例如,%d%s 等),或者是一個格式的陣列。如果未指定,會自動判斷格式。
  • $where_format (array|string):可選的。描述 where 格式。

成功時返回受影響的行數,如果沒有任何行被更新,則返回 0。失敗時返回 false

範例:

使用 update 更新資料
圖:使用 update 更新資料

更新 WordPress 資料庫的 wp_options 表中名為 Tiananmen 的選項的值。在上述範例中,$data 陣列定義了要更新的值,而 $where 陣列則定義了哪些行應該被更新。

方法:delete

function delete( $table, $where, $where_format = null ) {}

從資料庫中移除資料。

參數說明:

  • $table (string):目標資料表的名稱。
  • $where (array):確定哪些行應該被刪除的條件。鍵是欄位名稱,值是對應的資料。
  • $where_format (array|string):可選的。描述 where 格式。

成功時返回被刪除的行數。失敗時返回 false

範例:

使用 delete 刪除資料
圖:使用 delete 刪除資料

從 WordPress 資料庫的 wp_options 資料表中刪除名為 Tiananmen 的選項。在上述範例中,$where 陣列定義了哪些行應該被刪除。

方法:query

function query( $query ) {}

用於執行任何 SQL 查詢,不論是選擇欄位、插入、更新還是刪除資料的操作。

參數說明:

  • $query (string):要執行的 SQL 查詢字串。

此方法的返回值取決於查詢的類型:

  • 對於 SELECTSHOWDESCRIBEEXPLAIN 查詢,它將返回結果行的數量。
  • 對於其他類型的查詢(如 INSERTUPDATEDELETE 等),它將返回受影響的行的數量。
  • 查詢失敗時返回 false

範例:

使用 query 刪除資料
圖:使用 query 刪除資料

假設你想從 WordPress 資料庫的 wp_options 表中刪除名為 Tiananmen 的選項。

如果是接收來自外部來源的資料時。在這種情況下,必須使用 prepare 進行查詢的安全格式化,以避免被執行 SQL 注入攻擊。

屬性

名稱 說明
base_prefix 定義於 wp-config.php 中的原始前綴。對於多站點:如果您想獲得沒有附加部落格編號的前綴,請使用此選項。
$col_info 最近查詢結果的欄位資訊。
$insert_id 最近的 INSERT 查詢為 AUTO_INCREMENT 生成的 ID。
$last_error MySQL 生成的最近的錯誤訊息。
$last_query 最近已執行的查詢 SQL 語句。
$last_result 最近的查詢結果。
$num_queries 已執行的查詢數。
$num_rows 上一次查詢返回的行數。
$prefix 網站的設定 WordPress 資料表的前綴。
$queries 通過將 SAVEQUERIES 常數設置為 TRUE,您可以保存在資料庫上運行的所有查詢及其停止時間,並將作為陣列存儲在此變數中。
$show_errors 是否開啟顯示錯誤訊息。預設值為 TRUE

這些屬性都是公開屬性,可直接使用,但切勿修改它,以免不預期的錯誤發生。例如修改了 base_prefix,會造成找不到資料表的錯誤。

範例

echo $wpdb->last_query;

以上範例將印出最近一次的 SQL 查詢語句到畫面上。

總結

雖然 WordPress 本身已經提供了大量的內建函式供開發者進行直接操作和資料表關對應的新增、修改、刪除等動作,以及使用高階的 WP_Query 類別來包裝 wpdb 的各種複雜的查詢情境,但仍有不足的時候。

這時候 wpdb 可以讓我們直接與資料庫溝通,無論是最佳化查詢語句,或者用來操作我們設計外掛時自行產生的資料表。因此好好理解及學習 wpdb 的用法是十分有幫助的哦!

參考官方文件:


課後思考:

為什麼在下 SQL 查詢語句之前都要先使用 prepare 方法,如果不使用的話可能會發生什麼事?

前篇解答參考:

WP_Query 雖然在選項參數中提供了許多查詢的條件可以使用,通常查詢有索引的欄位,例如 post_typepost_name,不過當使用到沒有索引的欄位作為查詢條件的話,會影響效能。盡量避免這類的操作。


上一篇
Day 21 - 利用 WP_Query 類別來自定內容查詢
下一篇
Day 23 - 洞悉 WordPress 的物件快取 (Object Cache) 機制
系列文
從 0 到 100:WordPress 開發者的實戰手冊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言